package org.hawk.gwt.ppc.scanner.utils;

import java.util.List;

import org.hawk.gwt.ppc.utils.Name;

/**
 * Represents java variable declaration.
 * Holds all necessary data to get information about variable.
 * @author alex.bereznevatiy@gmail.com
 */
public class JavaVariable extends JavaStatement {
	private String name;
	private String type;
	private int index;
	private JavaScope scope;
	private boolean varArg;

	JavaVariable(String name,String type, int index,JavaScope scope) {
		this(name,type,index,scope,false);
	}
	
	public JavaVariable(String name, String type, int index,
			JavaScope scope, boolean varArg) {
		this.name = name;
		this.type = type;
		this.index = index;
		this.scope = scope;
		this.varArg = varArg;
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaStatement#setModifiers(java.util.List)
	 */
	@Override
	void setModifiers(List<JavaModifier> modifiers) {
		if (scope instanceof JavaTypeScope && ((JavaTypeScope)scope).isInterface()) {
			if (!modifiers.contains(JavaModifier.STATIC)) {
				modifiers.add(JavaModifier.STATIC);
			}
			if (!modifiers.contains(JavaModifier.PUBLIC)) {
				modifiers.add(JavaModifier.PUBLIC);
			}
			if (!modifiers.contains(JavaModifier.FINAL)) {
				modifiers.add(JavaModifier.FINAL);
			}
		}
		super.setModifiers(modifiers);
	}

	/**
	 * @param index
	 * @return <code>true</code> if this variable is accessible 
	 * at runtime at the specified index of iterator (token).
	 */
	public boolean isDeclaredAt(int index) {
		return this.index<=index;
	}
	/**
	 * @return <code>true</code> if this variable represents
	 * method variable length argument.
	 */
	public boolean isVarArg() {
		return varArg;
	}
	@Override
	public boolean equals(Object obj) {
		if(obj==this)return true;
		if(obj instanceof JavaVariable){
			JavaVariable var = (JavaVariable)obj;
			return var.getName().equals(getName())
				&&var.getTypeName().equals(getTypeName());
		}
		return false;
	}
	/**
	 * @return name this variable declared with
	 */
	public String getName() {
		return name;
	}
	/**
	 * @return type name of this variable including type parameters.
	 */
	public String getTypeName() {
		if(scope==null||Name.isFullName(type)){
			return type;
		}
		return scope.resolveType(type);
	}

	String getRawTypeName() {
		return type;
	}
	/**
	 * @return index of token where this variable is declared
	 */
	public int getIndex() {
		return index;
	}
	/**
	 * @return scope of this variable visibility
	 */
	public JavaScope getScope() {
		return scope;
	}

	void setScope(JavaScope scope) {
		this.scope = scope;
	}

	@Override
	public String toString() {
		return getTypeName()+' '+getName();
	}

	/*
	 * TODO: this method should be united with JavaMethod's one
	 */
	public JavaTypeScope getTypeDescriptor() {
		if (!(scope instanceof JavaNameResolver)) {
			return null;
		}
		JavaNameResolver resolver = (JavaNameResolver)scope;
		String pureType = Name.getPureClassName(type);
		JavaTypeVariable var = resolver.resolveTypeParameter(pureType);
		if (var != null) {
			JavaTypeScope result = var;
			String name = type;
			while (name.endsWith("[]")) {
				result = new JavaArrayGenericTypeScope(result, resolver);
				name = name.substring(0, name.length() - 2);
			}
			return result;
		}
		JavaTypeScope raw = resolver.resolveTypeDescriptor(type).getCompilationUnit();
		String params = Name.getTypeParameters(type);
		if (params.isEmpty()) {
			return raw;
		}
		return new JavaParameterizedGenericTypeScope(raw, resolver, params);
	}
}
